#include <stdio.h>
#include "hd_oled.h"
#include "delay.h"
#include "main.h"
#include "pd_h_flash.h"
#include "pd_h_usart.h"

#define OLED_I2C            1   // Select a protocol in I2C or SPI
#define OLED_I2C_HARDWARE   1   // Select hardware or software simulate
OLED *oled;

void SystemClock_Config(void);

int main(void) {

    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

#ifdef OLED_SPI
    GPIO sckGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_5);  // Clock, SPIx_SCK
    GPIO sdaGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_7);  // MOSI,  SPIx_MOSI
    GPIO resGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_3);  // Reset, General IO, the chip reset pin is better
    GPIO dcGPIO  = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_1);  // Data or Command, General IO
    GPIO csGPIO  = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_2);  // Chip Select(Slave Select), General IO
    SPIGPIO spiGPIO = SPIGPIOBuilder.build(&csGPIO, 1, &sckGPIO, &sdaGPIO, NULL);

#ifdef OLED_SPI_SIMULATE
    SPI *spi = (SPI *) SPISimulateBuilder.build(&spiGPIO);
    spi->init(spi);
#elif OLED_SPI_HARDWARE
    SPI *spi = (SPI *) SPIHardwareBuilder.build(SPI1, &spiGPIO);
    spi->init(spi);
#endif
    OLEDProtocolSPI *oledProtocolObj = OLEDProtocolSPIBuilder.build(spi, &resGPIO, &dcGPIO, &csGPIO);

#elif OLED_I2C
    GPIO sckGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_10);  // I2Cx_SCL
    GPIO sdaGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_11);  // I2Cx_SDA
#ifdef OLED_I2C_SIMULATE
    I2CSimulate i2CObj = I2CSimulateBuilder.build(&sckGPIO, &sdaGPIO);
#elif OLED_I2C_HARDWARE
    I2CHardware i2CObj = I2CHardwareBuilder.build(I2C2, &sckGPIO, &sdaGPIO);
#endif
    I2C *i2c = (I2C *) &i2CObj;
    i2c->init(i2c);
    OLEDProtocolI2C *oledProtocolObj = OLEDProtocolI2CBuilder.build(i2c);
#endif

    oled = OLED.new(OLED_SIZE_16, OLED_MODE_POSITIVE, (OLEDProtocol *) oledProtocolObj);
    oled->init(oled);

    oled->clear(oled);
    oled->refresh(oled);

    PD_H_USART1_Init();

    HD_OLED_Init(OLED_SIZE_16, OLED_MODE_POSITIVE);

    /* Flash write */
    uint32_t writeAddr = 0x0801FFE0;
    // 解锁
    PD_H_FLASH_Unlock();
    // 擦除
    uint32_t PageError;
    LL_FLASH_EraseInitTypeDef FLASH_EraseInitStruct;
    FLASH_EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    FLASH_EraseInitStruct.Banks = FLASH_BANK_1;
    FLASH_EraseInitStruct.Page = (writeAddr - FLASH_BASE) / FLASH_PAGE_SIZE;
    FLASH_EraseInitStruct.NbPages = 1;
    PD_H_FLASH_Erase(&FLASH_EraseInitStruct, &PageError);
    // 写入
    PD_H_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, writeAddr, 0x12345678);
    // 锁定
    PD_H_FLASH_Lock();

    /* Flash read */
    HD_OLED_ShowString(0, 0, "FLASH");
    HD_OLED_ShowString(0, 16, "PACKAGE:");
    HD_OLED_ShowBinNum(64, 16, PD_H_FLASH_ReadHalfWord(PACKAGE_BASE), 5);

    HD_OLED_ShowString(0, 32, "SIZE:");
    HD_OLED_ShowNum(40, 32, PD_H_FLASH_ReadHalfWord(FLASHSIZE_BASE), 5);

    HD_OLED_ShowString(0, 48, "UID:");
    uint64_t uid;
    uid = PD_H_FLASH_ReadWord(UID_BASE);
    uid <<= 32;
    uid |= PD_H_FLASH_ReadWord(UID_BASE + 4);
    HD_OLED_ShowHexNum(56, 48, uid, 8);
    uid = PD_H_FLASH_ReadWord(UID_BASE + 8);
    HD_OLED_ShowHexNum(32, 48, (uint32_t) uid, 4);

    while (1) {}

}


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void) {
    LL_FLASH_SetLatency(LL_FLASH_LATENCY_5);
    while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_5) {
    }
    LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
    LL_RCC_HSI_SetCalibTrimming(16);
    LL_RCC_HSI_Enable();

    /* Wait till HSI is ready */
    while (LL_RCC_HSI_IsReady() != 1) {

    }
    LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_8, 168, LL_RCC_PLLP_DIV_2);
    LL_RCC_PLL_Enable();

    /* Wait till PLL is ready */
    while (LL_RCC_PLL_IsReady() != 1) {

    }
    LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
    LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_4);
    LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);
    LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);

    /* Wait till System clock is ready */
    while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {

    }
    LL_Init1msTick(168000000);
    LL_SetSystemCoreClock(168000000);
}


#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line) {
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
       ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}

#endif /* USE_FULL_ASSERT */